package com.nodecollege.cloud.core.interceptor;

import com.nodecollege.cloud.common.UpmsConstants;
import com.nodecollege.cloud.common.annotation.AdminLoginAnnotation;
import com.nodecollege.cloud.common.annotation.ApiAnnotation;
import com.nodecollege.cloud.common.annotation.UserLoginAnnotation;
import com.nodecollege.cloud.common.aop.NCAop;
import com.nodecollege.cloud.common.constants.NCConstants;
import com.nodecollege.cloud.common.enums.ErrorEnum;
import com.nodecollege.cloud.common.exception.UpmsException;
import com.nodecollege.cloud.common.model.LoginAdmin;
import com.nodecollege.cloud.common.model.LoginUser;
import com.nodecollege.cloud.common.model.po.UpmsApiVisit;
import com.nodecollege.cloud.common.utils.NCUtils;
import com.nodecollege.cloud.common.utils.ThreadAdminUtils;
import com.nodecollege.cloud.common.utils.ThreadPoolFactory;
import com.nodecollege.cloud.common.utils.ThreadUserUtils;
import com.nodecollege.cloud.service.AdminLoginService;
import com.nodecollege.cloud.service.ApiVisitService;
import com.nodecollege.cloud.service.LoginService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.concurrent.ExecutorService;

public class GlobalLoginInterceptor extends HandlerInterceptorAdapter {

    private static final Logger logger = LoggerFactory.getLogger(GlobalLoginInterceptor.class);

    private static final ExecutorService GLOBAL_LOGIN_EXECUTOR = ThreadPoolFactory.getInstance();

    @Autowired
    private AdminLoginService adminLoginService;

    @Autowired
    private LoginService loginService;

    @Autowired
    private ApiVisitService apiVisitService;

    @Value("${spring.application.name}")
    private String applicationName;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        // 清除以前的登陆数据
        ThreadAdminUtils.resetLoginAdmin();

        String requestUrl = request.getRequestURI();
        String accessToken = request.getHeader(UpmsConstants.HEADER_ACCESS_TOKEN);
        // 访问来源
        String accessSource = request.getHeader(UpmsConstants.HEADER_ACCESS_SOURCE);
        String requestIp = getRequestIp(request);
        logger.info("访问地址：{}，访问token：{}, 访问来源：{}，访客ip：{}", requestUrl, accessToken, accessSource, requestIp);
        NCUtils.nullOrEmptyThrow(accessSource, new UpmsException("8888", "访问来源必填！"));

        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            // 取method 有无用户登陆注解
            AdminLoginAnnotation adminLoginM = handlerMethod.getMethodAnnotation(AdminLoginAnnotation.class);
            UserLoginAnnotation userLoginM = handlerMethod.getMethodAnnotation(UserLoginAnnotation.class);
            ApiAnnotation apiM = handlerMethod.getMethodAnnotation(ApiAnnotation.class);
            if (adminLoginM == null) {
                adminLoginM = AnnotationUtils.findAnnotation(handlerMethod.getBean().getClass(), AdminLoginAnnotation.class);
            }
            if (userLoginM == null) {
                userLoginM = AnnotationUtils.findAnnotation(handlerMethod.getBean().getClass(), UserLoginAnnotation.class);
            }
            UpmsApiVisit upmsApiVisit = new UpmsApiVisit();
            upmsApiVisit.setApplicationName(applicationName);
            upmsApiVisit.setControllerName(NCAop.getConciseMethodName(handlerMethod.getMethod().getDeclaringClass().getName()));
            upmsApiVisit.setApiUrl(requestUrl);
            upmsApiVisit.setRequestIp(requestIp);
            upmsApiVisit.setAccessSource(accessSource);
            // 校验来源不为1时，需要强制校验来源信息
            if (apiM != null) {
                if (NCConstants.INT_2.equals(apiM.accessSource())) {
                    if (accessSource.equals(UpmsConstants.ACCESS_SOURCE_NGINX)) {
                        throw new UpmsException("-1", "该接口只能内部调用！");
                    }
                    // 内部调用不需要校验登陆信息
                    GLOBAL_LOGIN_EXECUTOR.submit(() -> apiVisitService.addApiVisitCache(upmsApiVisit));
                    return super.preHandle(request, response, handler);
                }
                if (NCConstants.INT_3.equals(apiM.accessSource()) && !accessSource.equals(UpmsConstants.ACCESS_SOURCE_NGINX)) {
                    throw new UpmsException("-1", "该接口只能通过nginx调用！");
                }
            }
            if (adminLoginM != null) {
                adminLoginInfoHandle(accessToken, applicationName + requestUrl);
            } else if (userLoginM != null) {
                LoginUser loginUser = userLoginInfoHandle(accessToken, userLoginM.checkDataPower(), applicationName + requestUrl);
                upmsApiVisit.setTenantId(loginUser.getTenantId());
            }

            GLOBAL_LOGIN_EXECUTOR.submit(() -> apiVisitService.addApiVisitCache(upmsApiVisit));
        }

        return super.preHandle(request, response, handler);
    }

    /**
     * 添加当前登陆管理员线程数据
     *
     * @param accessToken
     */
    private void adminLoginInfoHandle(String accessToken, String url) {
        LoginAdmin loginAdmin = adminLoginService.getAdminInfo(accessToken);
        if (NCUtils.isNotNullOrNotEmpty(loginAdmin)) {
            loginAdmin.checkPower(url);
            ThreadAdminUtils.setLoginAdmin(loginAdmin);
        } else {
            ErrorEnum.LOGIN_TIME_OUT.throwMsg();
        }
    }

    /**
     * 添加当前登陆用户线程数据
     *
     * @param accessToken
     */
    private LoginUser userLoginInfoHandle(String accessToken, Boolean checkDataPower, String url) {
        LoginUser loginUser = loginService.getUserInfo(accessToken);
        if (loginUser != null) {
            if (checkDataPower) {
                loginUser.setApiDataPower(loginUser.getDataPower().getApiDataPower(url, loginUser.getShowAllRole()));
            }
            ThreadUserUtils.setLoginUser(loginUser);
        } else {
            ErrorEnum.LOGIN_TIME_OUT.throwMsg();
        }
        return loginUser;
    }


    /**
     * 获取访客ip
     *
     * @param request
     * @return
     */
    public static String getRequestIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip != null && ip.length() != 0 && !NCConstants.UNKNOWN.equalsIgnoreCase(ip)) {
            //多次反向代理后会有多个ip值，第一个ip才是真实ip
            int index = ip.indexOf(",");
            if (index != -1) {
                ip = ip.substring(0, index);
            }
        }
        if (ip == null || ip.length() == 0 || NCConstants.UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Real-IP");
        }
        if (ip == null || ip.length() == 0 || NCConstants.UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || NCConstants.UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || NCConstants.UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || NCConstants.UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || NCConstants.UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }
}